#!/usr/bin/env bash
# xin.sh - Gentoo installation script
#
## License
#
# Copyright (c) 2021 Damon Kwok <damon-kwok@outlook.com>
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
## About
#
# While this script does assist with installing Gentoo, it is NOT a
# replacement for reading and understanding the Gentoo Handbook for
# your system.  See https://exherbo.org/docs/gettingstarted.html for 
# information on getting started with Gentoo.
#
# This file uses inline documentation whenever possible to preserve
# context, situational or otherwise.  Please read the entire file
# before continuing to ensure you are aware of how this script
# functions, as undesired behaviors for your use-case may exist.
#
# This script does NOT automatically partition your disk(s).  You MUST
# partition and mount your disk(s) before running this script.
#
##

set -e -o pipefail

alias fold="fold -s -w ${COLUMNS:-$(stty size|awk '{print $2}')}"
alias make="make -s -j $(grep -c ^processor /proc/cpuinfo)"

# Width-respecting print
OS_NAME="Exherbo"
function print() {
    echo "<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>"
    echo "<> ${OS_NAME}: $@" | fold
    #echo "<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>"
    echo ""
}

# This section describes variables that will define the resulting
# system.  Some are specific to this script, though others should look
# familiar, aside from their prefix.
#
#   XIN_CONFIG_URL
#
#       The base URL where the source of this repository (or your fork)
#       is available.  This allows this script to download all needed
#       configuration files rather than you having to retrieve them
#       manually.
#
#   XIN_HOSTNAME
#
#       The hostname to set on the resulting system after the
#       installation has completed.
#
#   XIN_LOCALE
#
#       The LOCALE to set based on your preference.  All language
#       defaults such as this default to some variant of en_US.UTF-8
#       for simplicity of design.
#
#   XIN_VIDEO_CARDS
#
#       The value of VIDEO_CARDS that will be set in
#       '/etc/portage/make.conf', which may either be left blank or be
#       configured as mentioned in the following url:
#
#       https://wiki.gentoo.org/wiki//etc/portage/make.conf#VIDEO_CARDS
#
#       You most likely should use "amdpu", "intel", or "nvidia".
#
#   XIN_TIMEZONE
#
#       The timezone to set on the resulting system after the
#       installation has completed.  This is set to America/New_York
#       by default for simplicity of design.

XIN_CONFIG_URL="https://gitee.com/damon-kwok/xin/raw/main"
XIN_HOSTNAME="exherbo"
XIN_LOCALE="en_US.UTF-8 UTF-8"
XIN_TIMEZONE="Asia/Shanghai"
XIN_VIDEO_CARDS="virtualbox"


# This section describes how the kernel will be built, and whether the
# user will be prompted to configure their kernel. The two mentioned
# variables may be used together to suit a variety of use-cases.
#
#   XIN_KERNEL_AUTOBUILD
#
#     When 'true', this implies that the user does not want to perform
#     any manual configuration of the kernel, regardless of whether a
#     configuration file was provided. '$ make menuconfig' will NOT be
#     run before compiling the kernel.
#
#     When any value OTHER than 'true', this implies that the use DOES
#     want to perform manual configuration on the kernel, regardless
#     of whether a configuration file was provided. '$ make menuconfig'
#     will be run before compiling the kernel.

XIN_KERNEL_AUTOBUILD="true"


# TODO: I would like this section to be where a user defines their
# partition schema, specifically for configuring GRUB2.
#
#   XIN_PARTITION_BOOT
#
#       Missing description
#
#   XIN_PARTITION_SWAP
#
#       Missing description
#
#   XIN_PARTITION_SWAPFILE_SIZE
#
#       The size of the swapfile to create.  This is only used if
#       XIN_PARTITION_SWAP is set to "/swapfile", and defaults to 4G.

XIN_PARTITION_BOOT="/dev/sda"
XIN_PARTITION_SWAP="/dev/sda2"
XIN_PARTITION_SWAPFILE_SIZE="20G"
XIN_PARTITION_ROOT="/dev/sda3"

# This section determines the current system architecture, which later
# is used to download the correct Stage 3 archive.  Depending on the
# architecture, there may be implications in which directory we should
# retrieve our Stage 3 from.

case "$(uname -m)" in
    i686|x86|x86_32)
        XIN_CPU_DIR="x86"
        XIN_CPU_ARCH="i686"
        ;;

    amd64|x86_64)
        XIN_CPU_DIR="x86_64"
        XIN_CPU_ARCH="x86_64"
        ;;

    *)
        print "error: your architecture is not yet defined." \
              "Submit an issue with the output of 'uname -m' to" \
              "https://github.com/jcmdln/xin so it may be reviewed"
        print "Exiting..."
        exit 1
esac

OUT_PREREQUISITES() {
    if [ -v "$XIN_PARTITION_BOOT" ]; then
        print "error: XIN_PARTITION_BOOT is not set!"
        print "please ensure you have partitioned and mounted" \
              "your disks, as well as updated the variables associated" \
              "with the required partitions."
        print "Exiting..."
        exit 1
    fi

    AUTO_MOUNT $XIN_PARTITION_ROOT /mnt/exherbo

    if [ ! -e /mnt/exherbo ]; then
        print "error: '/mnt/exherbo' does not exist!"
        print "'/mnt/exherbo' is required for us to build" \
              "Gentoo, as it is referred to later in this script." \
              "Please ensure your mounted partitions are correct."
        print "Exiting..."
        exit 1
    fi
}

OUT_BOOTSTRAP() {
    cd /mnt/exherbo
    print "correct system time via ntpd... "
    ntpdate -s ntp.aliyun.com
    hwclock -w
    #if [ -n "$(command -v ntpd)" ]; then
    #    ntpd -q -g
    #else
    #    print "warning: ntpd not found!"
    #    print "assuming system time is correct"
    #fi

    print "Downloading and extracting Stage3 tarball..."
    if [ -n "$(command -v curl)" ]; then
        stage3_ver="https://dev.exherbo.org/stages/exherbo-${XIN_CPU_ARCH}-pc-linux-gnu-current.tar.xz"
        if [ ! -e "$exherbo-${XIN_CPU_ARCH}-pc-linux-gnu-current.tar.xz" ]; then
            # curl -O "$stage3_ver"
            scp damon@192.168.3.240:/home/damon/share/exherbo-x86_64-pc-linux-gnu-current.tar.xz .
            # curl -O https://dev.exherbo.org/stages/sha1sum
            # grep exherbo-x86_64-pc-linux-gnu-current.tar.xz sha1sum | sha1sum -c
        fi
        if [ ! -d "/mnt/exherbo/boot/" ]; then
            tar -xpvf exherbo-*.tar.xz --xattrs --numeric-owner -C /mnt/exherbo
            # rm -rf "$PWD/exherbo-*"
        fi
        unset stage3_ver
    else
        print "error: curl not present!"
        print "Exiting..."
        exit 1
    fi
}

OUT_DOWNLOAD_CONF() {
    print "ensure script is available in /mnt/exherbo... "
    if [ ! -d /mnt/exherbo/xin/ ]; then
        cp -rf /xin /mnt/exherbo/
        cd /mnt/exherbo/xin/
        git pull
    fi

    print "Downloading portage configuration files..."
    config_files="
        etc/portage/make.conf
        etc/portage/sets/xin-base
        etc/portage/sets/xin-nscde
        etc/portage/sets/xin-cinnamon
        etc/portage/sets/xin-workstation
    "

    #for config_file in $config_files; do
    #    local config_dir="`dirname /mnt/exherbo/$config_file`"
    #    [ -e ${config_dir} ] && [ ! -d ${config_dir} ] && rm ${config_dir}
    #    mkdir -p ${config_dir}
    #    [ -e "/mnt/exherbo/$config_file" ] && rm -rf "/mnt/exherbo/$config_file"
    #    wget -q "$XIN_CONFIG_URL/$config_file" -O "/mnt/exherbo/$config_file"
    #done; unset config_dir config_files config_file

    #print "Updating make.conf..."
    #sed -i "
    #    s/^MAKEOPTS.*$/MAKEOPTS\=\"-j$(grep -c ^processor /proc/cpuinfo)\"/;
    #    s/^VIDEO_CARDS.*$/VIDEO_CARDS=\"$XIN_VIDEO_CARDS\"/;
    #" /mnt/exherbo/etc/portage/make.conf

    # print "Copying '/etc/resolv.conf'..."
    # echo "nameserver 8.8.8.8" > /mnt/exherbo/etc/resolv.conf

    #echo "select china mirrors:"
    #mirrorselect -i -o >> /mnt/exherbo/etc/portage/make.conf

    print "Copying '/etc/resolv.conf'..."
    cp --dereference /etc/resolv.conf /mnt/exherbo/etc/

    print "Copying /usr/lib/grub/x86_64-efi/"
    cp --dereference -rf /usr/lib/grub/x86_64-efi/ /mnt/exherbo/usr/lib/grub/x86_64-efi/
}

OUT_CHANGE_ROOT() {
    print "Mounting hardware devices..."
    for hw_mountpoint in $(echo proc sys dev); do
        if [ -e /mnt/exherbo/"$hw_mountpoint" ]; then
            case "$hw_mountpoint" in
                proc)
                    mount -t proc /proc /mnt/exherbo/proc
                    ;;

                sys)
                    mount --rbind /sys /mnt/exherbo/sys
                    mount --make-rslave /mnt/exherbo/sys
                    ;;

                dev)
                    mount --rbind /dev /mnt/exherbo/dev
                    mount --make-rslave /mnt/exherbo/dev
                    ;;

                *)
                    print "error: $hw_mountpoint: Improper " \
                          "hardware device"
                    exit 1
            esac
        else
            print "error: $hw_mountpoint unable to be mounted!" \
                  "Exiting..."
            exit 1
        fi
    done; unset hw_mountpoint

    print "Setting up swapfile..."
    if [ -v "$XIN_PARTITION_SWAP" ]; then
        if [ "$XIN_PARTITION_SWAP" = "/swapfile" ]; then
            fallocate -l "/mnt/exherbo/$XIN_PARTITION_SWAP_SIZE"
        fi

        mkswap "/mnt/exherbo/$XIN_PARTITION_SWAP"
        swapon "/mnt/exherbo/$XIN_PARTITION_SWAP"
        echo "$XIN_PARTITION_SWAP none swap sw 0 0" >> /mnt/exherbo/etc/fstab
    fi

    print "Chroot'ing into /mnt/exherbo..."
    chroot \
        /mnt/exherbo /usr/bin/env -i \
        HOME="/root" \
        MANPATH="/usr/man:/usr/share/man:/usr/local/man:/usr/local/share/man" \
        MANPATH="$MANPATH:$HOME/man:$HOME/.local/man:$HOME/.local/share/man" \
        PS1="(chroot) $PS1" \
        PATH="/sbin:/usr/sbin:/opt/sbin:/usr/local/sbin/:/bin:/usr/bin" \
        PATH="$PATH:/opt/bin:/usr/local/bin:$HOME/bin:$HOME/.local/bin" \
        TERM="$TERM" \
        /bin/bash --login
}

IN_INSTALL() {
    print "Updating environment..."
    source /etc/profile
    export PS1="(chroot) $PS1"

    print "cave sync..."
    cave sync

    print "cave resolve world"
    cave resolve world -x

    print "Setting timezone..."
    rm -rf /etc/localtime
    ln -s /usr/share/zoneinfo/${XIN_TIMEZONE} /etc/localtime

    print "Setting locale..."
    #localedef -i en_US -f ISO-8859-1 en_US
    localedef -i en_US -f UTF-8 en_US.UTF-8
    echo LANG="en_US.UTF-8" > /etc/env.d/99locale

    #print "Emerging base system packages..."
    #emerge @xin-base --autounmask-write
    #etc-update --automode -3
    #emerge -v @xin-base

    print "Install base utils."
    cave resolve sys-boot/grub app-admin/sudo net-misc/dhcpcd sys-process/cronie -x
    # net-apps/NetworkManager

    print "Adding services to OpenRC..."
    #rc-update add dhcpcd default
    #rc-update add cronie default

    print "Setting hostname..."
    echo "hostname=$XIN_HOSTNAME" > /etc/conf.d/hostname

    IN_INSTALL_KERNEL
    cave resolve repository/hardware -1x
    cave resolve firmware/linux-firmware -x

    if grep -Rqi 'intel' /proc/cpuinfo; then
        print "Emerging intel-microcode"
        # emerge -v --quiet-build intel-microcode
        cave resolve firmware/intel-microcode sys-apps/iucode-tool -x
    fi
    
    print "Setting root password..."
    passwd

    read -p "Setup a standard user? [Y/N]: " setup_user
    if echo "$setup_user" | grep -iq "^y"; then
        print "Creating user account"
        read -p "Username: " username
        useradd -m -G adm,disk,wheel,cdrom,audio,video,usb,users -s /bin/bash ${username}
        passwd ${username}
        sed -i '/ALL ALL=(ALL) ALL/a\${username} ALL=(ALL) ALL' /etc/sudoers
    fi

    print "Installation complete."
}

AUTO_MOUNT() {
    local part=$1
    local point=$2

    mkdir -p $point
    local partname=`basename $part`
    local info=`lsblk | grep $partname`
    echo "info:$info"
    local str="`echo $info | cut -d ' ' -f 7`"
    echo "str:|${str##*( )}|"
    if [ "${str##*( )}" == "" ]; then
        mount $part $point
    fi
}

AUTO_MKFS() {
    local part=$1
    local point=$2
    umount $part
    mkfs $2 $3 $4 $5
}

AUTO_DOWNLOAD() {
    local oldir=`pwd`
    cd $1
    local dir="`pwd`"
    local url="$2"
    
    if [ ! -e "`basename ${url}`" ]; then
        curl -O ${url}
    fi
    cd $oldir
}

XIN_KERNEL_VERSION=5.14.10
# XIN_CPU_NUM=`cat /proc/cpuinfo | grep processor | wc -l`
# XIN_CPU_NUM=`getconf _NPROCESSORS_ONLN`
XIN_CPU_NUM=`grep --count ^processor /proc/cpuinfo`
# XIN_CPU_NUM=`$(grep -c ^processor /proc/cpuinfo 2>/dev/null || sysctl -n hw.ncpu)`

IN_INSTALL_KERNEL() {
    # echo"${filename%.tar.xz}"
    print "Configuring Linux kernel..."
    #AUTO_DOWNLOAD / "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-${XIN_KERNEL_VERSION}.tar.xz"
    # cp /var/cache/paludis/distfiles/linux-5.14.tar.xz .
    #tar -xvJf /linux-${XIN_KERNEL_VERSION}.tar.xz
    tar -xvJf /var/cache/paludis/distfiles/linux-5.14.tar.xz -C /usr/src/
    cd /usr/src/linux-* 

    if [ "$XIN_KERNEL_AUTOBUILD" = "true" ]; then
        make defconfig
    elif [ "$XIN_KERNEL_AUTOBUILD" = "false" ]; then
        make defconfig
        make menuconfig
    else
        print "Error: AutoKernel isn't true or false. Exiting..."
    fi

    print "Compiling Linux kernel and modules..."
    time make INSTALL_MOD_STRIP=1 -j${XIN_CPU_NUM}&&
        make modules install -j${XIN_CPU_NUM} &&
        make install &&
        # time make distclean &&
        cd /

    print "Installing Grub to $XIN_PARTITION_BOOT..."
    if [ -d /sys/fireware/efi ]; then
        AUTO_MOUNT /dev/sda1 /boot/efi
    else
        AUTO_MOUNT /dev/sda1 /boot
    fi
    grub-install "$XIN_PARTITION_BOOT"
    mkdir -p /boot/grub/
    grub-mkconfig -o /boot/grub/grub.cfg
}

case $1 in
    -b|--bootstrap)
        OUT_PREREQUISITES
        OUT_BOOTSTRAP
        OUT_DOWNLOAD_CONF
        OUT_CHANGE_ROOT
        ;;

    -i|--install)
        IN_INSTALL
        ;;

    -vpn)
        openconnect https://fmt1.link.ac.cn --authgroup smart ;;

    -c|--chroot)
        OUT_PREREQUISITES
        if [ -d /sys/fireware/efi ]; then
            AUTO_MOUNT /dev/sda1 /boot/efi
        else
            AUTO_MOUNT /dev/sda1 /boot
        fi
        OUT_DOWNLOAD_CONF
        OUT_CHANGE_ROOT
        ;;

    -push|push)
        local ipaddr="192.168.0.104"
        if [ "$2" !="" ]; then
            ipaddr=$2
        fi
        rsync -avz -e ssh /var/cache/binpkgs/ damon@${ipaddr}:/home/damon/binpkgs/exherbo/
        ;;

    -sync|sync)
        git pull
        cp -rf etc/* /etc/
        mirrorselect -i -o >> /etc/portage/make.conf
        ;;

    *)
        echo "$OS_NAME minimal installation script"
        echo "  -b. --bootstrap    Bootstrap the stage3 tarball"
        echo "  -i, --install      Install Gentoo"
        echo "  -c, --chroot       Chroot to /dev/sda3, continue install..."
esac
